<!DOCTYPE html>
<html>

<head>
    <title>InstancedMesh Test</title>
    <script type="text/javascript" src="https://unpkg.com/dat.gui@0.7.6/build/dat.gui.min.js"></script>
    <link type="text/css" rel="stylesheet" href="https://unpkg.com/maptalks/dist/maptalks.css">
    <script type="text/javascript" src="https://unpkg.com/maptalks/dist/maptalks.js"></script>
    <script type="text/javascript" src="https://unpkg.com/@maptalks/gl/dist/maptalksgl.js"></script>
    <script type="text/javascript" src="https://unpkg.com/three@0.138.0/build/three.min.js"></script>
    <script type="text/javascript" src="https://unpkg.com/maptalks.three@latest/dist/maptalks.three.js"></script>
    <script type="text/javascript" src="https://unpkg.com/dat.gui@0.7.6/build/dat.gui.min.js"></script>
    <style>
        html,
        body {
            margin: 0px;
            height: 100%;
            width: 100%;
        }

        #map {
            width: 100%;
            height: 100%;
            background-color: #000;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>

        var map = new maptalks.Map("map", {
            center: [121.22963692336634, 31.28985457681071],
            zoom: 17,
            pitch: 0,
            // bearing: 180,

            centerCross: true,
            doubleClickZoom: false,
            baseLayer: new maptalks.TileLayer('tile', {
                urlTemplate: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
                subdomains: ['a', 'b', 'c', 'd'],
                attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
            })
        });

        // the ThreeLayer to draw buildings
        var threeLayer = new maptalks.ThreeLayer('t', {
            forceRenderOnMoving: true,
            forceRenderOnRotating: true
            // animation: true
        });
        threeLayer.prepareToDraw = function (gl, scene, camera) {
            var light = new THREE.DirectionalLight(0xffffff);
            light.position.set(0, -10, 10).normalize();
            scene.add(light);
            scene.add(new THREE.AmbientLight(0xffffff, 0.5));
            addCircle(scene);
            animation();

        };
        const sceneConfig = {
            postProcess: {
                enable: true,
                antialias: { enable: true }
            }
        };
        const groupLayer = new maptalks.GroupGLLayer('group', [threeLayer], { sceneConfig });
        groupLayer.addTo(map);

        function randomCoordiantes() {
            const { x, y } = map.getCenter();
            const coordinates = [];
            while (coordinates.length < 10000) {
                const x1 = Math.random() * 0.01 + x, y1 = Math.random() * 0.01 + y;
                coordinates.push([x1, y1, 0]);
            }
            return coordinates;
        }

        function addCircle() {
            const material = new THREE.MeshLambertMaterial({ color: '#fff', transparent: true });
            const highlightmaterial = new THREE.MeshLambertMaterial({ color: 'red' });
            const coordinates = randomCoordiantes().map(c => {
                return new maptalks.Coordinate(c);
            });
            const data = coordinates.map((c, index) => {
                return {
                    id: maptalks.Util.GUID(),
                    name: 'hello-' + index + 1
                };
            })
            const mesh = new InstancedMesh(coordinates, { radius: 1, data }, material, threeLayer);
            // tooltip test
            mesh.setToolTip('hello', {
                showTimeout: 0,
                // eventsPropagation: true,
                dx: 10
            });


            //infowindow test
            mesh.setInfoWindow({
                content: 'hello',
                title: 'message',
                animationDuration: 0,
                autoOpenOn: false
            });
            threeLayer.addMesh(mesh);

            map.setCenter(mesh.getOptions().center);
            mesh.on('mouseover mouseout mousemove click', function (e) {
                const eventType = e.type;
                if (this.instanceId === undefined) {
                    return;
                }
                if (eventType === 'mouseover') {
                    this.getObject3d().setColorAt(this.instanceId, highlightmaterial.color);
                    this.getObject3d().instanceColor.needsUpdate = true;
                }
                if (eventType === 'mouseout') {
                    this.getObject3d().setColorAt(this.instanceId, material.color);
                    this.getObject3d().instanceColor.needsUpdate = true;
                }
                const data = this.getOptions().data[this.instanceId];
                // override tooltip
                if (e.type === 'mousemove' && data) {
                    const tooltip = this.getToolTip();
                    tooltip._content = `${data.name}`;
                }
                //override infowindow
                if (e.type === 'click' && data) {
                    const infoWindow = this.getInfoWindow();
                    infoWindow.setContent(`name:${data.name}`);
                    if (infoWindow && (!infoWindow._owner)) {
                        infoWindow.addTo(this);
                    }
                    this.openInfoWindow(e.coordinate);

                }
            })
        }

        function animation() {
            // layer animation support Skipping frames
            threeLayer._needsUpdate = !threeLayer._needsUpdate;
            if (threeLayer._needsUpdate) {
                threeLayer.redraw();
            }
            requestAnimationFrame(animation);
        }

        function getCenterOfPoints(coordinates) {
            let sumX = 0, sumY = 0;
            const len = coordinates.length;
            for (let i = 0; i < len; i++) {
                const { coordinate, lnglat, lnglats, xy, xys } = coordinates[i];
                const c = coordinate || lnglat || lnglats || xy || xys || coordinates[i];
                let x, y;
                if (Array.isArray(c)) {
                    x = c[0];
                    y = c[1];
                } else if (c instanceof maptalks.Coordinate) {
                    x = c.x;
                    y = c.y;
                }
                sumX += x;
                sumY += y;
            }
            return new maptalks.Coordinate(sumX / len, sumY / len);
        }


        const OPTIONS = {
            radius: 10,
            altitude: 0
            // ...
        }

        class InstancedMesh extends maptalks.BaseObject {
            constructor(coordinates, options, material, layer) {
                const center = getCenterOfPoints(coordinates);
                options = maptalks.Util.extend({ center, layer }, OPTIONS, options);

                super();
                this._initOptions(options);
                const { radius, altitude } = options;
                const r = layer.distanceToVector3(radius, radius).x;
                const geometry = new THREE.CircleGeometry(r, 50);
                //create inner object3d
                this._createInstancedMesh(geometry, material, coordinates.length);
                const matrix = new THREE.Matrix4();
                const centerPt = layer.coordinateToVector3(center);
                coordinates.forEach((coordinate, index) => {
                    const pt = layer.coordinateToVector3(coordinate);
                    pt.sub(centerPt);
                    if (coordinate.z) {
                        pt.z = layer.altitudeToVector3(coordinate.z).x;
                    }
                    matrix.setPosition(pt.x, pt.y, pt.z);
                    this.getObject3d().setMatrixAt(index, matrix);
                    this.getObject3d().setColorAt(index, material.color);
                });
                const z = layer.altitudeToVector3(altitude, altitude).x;
                centerPt.z = z;
                this.getObject3d().position.copy(centerPt);
            }
        }

    </script>
</body>

</html>